#!/usr/bin/env python

from system.networkNode import NetworkNode

class Server(NetworkNode):
    def __init__(self):
        NetworkNode.__init__(self)
        
        self.shutdown = False
        self.state = {}
        self.clients = set()
        self.varSetters = {}
        self.varGetters = {}
        self.moduleClients = {}
        self.moduleCounts = {}
        self.objectActions = {}
        self.actionClients = {}
        self.rpcClients = {}
        self.subscriptionClients = {}
        
    def kill(self):
        for client in self.clients:
            self.send("shutdown",None,client)
        NetworkNode.kill(self)
        
    def dist(self, cmd, msg, notTo, outOf = None):
        for client in filter(lambda c: c != notTo, outOf or self.clients):
            self.send(cmd,msg,client)
            
    def receive_shutdown(self, msg, sender):
        print "got shutdown from", sender
        self.shutdown = True
        
    def receive_reg(self, msg, sender):
        if sender not in self.clients:
            print "got client", sender
            for module in msg:
                self.moduleClients.setdefault(module,[]).append(sender)
            self.clients.add(sender)
            self.send("regAck",self.state,sender)
            
    def receive_unreg(self, msg, sender):
        if sender in self.clients:
            print "lost client", sender
            self.clients.remove(sender)
            for module in self.moduleClients.keys():
                if sender in self.moduleClients[module]:
                    self.moduleClients[module].remove(sender)
            
    def receive_makeObjectName(self, msg, sender):
        if msg not in self.state:
            name = msg
        else:
            i = 2
            name = "%s_%d" % (msg,i)
            while name in self.state:
                i += 1
                name = "%s_%d" % (msg,i)
        print "make object", name
        self.state[name] = {}
        self.send("objectName",(msg,name),sender)
        self.dist("makeObject",name,sender)
        
    def receive_deleteObject(self, msg, sender):
        assert msg in self.state, "can't delete unknown object %s" % msg
        print "delete object", msg
        self.state.pop(msg)
        self.dist("deleteObject",msg,sender)
        
    def receive_requestVar(self, msg, sender):
        objectName, varName = msg
        self.varGetters.setdefault((objectName,varName),set()).add(sender)
        self.send("requestVarAck",msg,sender)
        if objectName in self.state and varName in self.state[objectName]:
            self.send('setVar',(objectName,varName,self.state[objectName][varName]),sender)
        
    def receive_setVar(self, msg, sender):
        objectName, varName, val = msg
        if (objectName, varName) in self.state:
            setter = self.varSetters.setdefault((objectName,varName),sender)
            assert setter == sender, "race condition for %s.%s" % (objectName, varName)
        assert objectName in self.state, "can't set var %s in unknown object %s" % (varName, objectName)
        self.state[objectName][varName] = val
        if (objectName,varName) in self.varGetters:
            self.dist("setVar",(objectName,varName,val),sender,self.varGetters[(objectName,varName)])
            
    def receive_distSetVar(self, msg, sender):
        self.dist("setVar",msg,sender)
            
    def makeActionName(self, objectName, actionType, requestedName):
        self.objectActions.setdefault(objectName,set())
        actionName = requestedName or actionType
        if actionName in self.objectActions[objectName]:
            i = 2
            name = "%s_%d" % (actionName, i)
            while name in self.objectActions[objectName]:
                i += 1
                name = "%s_%d" % (actionName, i)
            actionName = name
        self.objectActions[objectName].add(actionName)
        return actionName
            
    def receive_runAction(self, msg, sender):
        objectName, actionType, requestedName, args = msg
        actionName = self.makeActionName(objectName,actionType,requestedName)
        module = actionType.split('.')[0]
        assert self.moduleClients.get(module), "nobody runs module %s" % module
        moduleCount = self.moduleCounts.setdefault(module,0)
        self.moduleCounts[module] += 1
        runner = self.moduleClients[module][moduleCount % len(self.moduleClients[module])]
        self.actionClients[(objectName,actionName)] = runner
        self.send("runAction",(objectName,actionType,actionName,args),runner)
        self.send("actionName",(objectName,actionType,requestedName,actionName),sender)
        
    def receive_publish(self, msg, sender):
        cmd, args = msg
        if cmd in self.subscriptionClients:
            self.dist("publish",msg,sender,self.subscriptionClients[cmd])
        
    def receive_subscribe(self, msg, sender):
        self.subscriptionClients.setdefault(msg,set()).add(sender)
        
    def receive_unsubscribe(self, msg, sender):
        self.subscriptionClients[msg].remove(sender)
        
    def receive_rpc(self, msg, sender):
        objectName, actionName, cmd, args = msg
        assert objectName in self.state, "can't run rpc for unknown object %s" % objectName
        assert (objectName,actionName) in self.actionClients, "no actionClient for %s %s" % (objectName, actionName)
        self.rpcClients.setdefault((objectName,actionName,cmd),[]).append(sender)
        self.send("rpc",msg,self.actionClients[(objectName,actionName)])
        
    def receive_rpcResult(self, msg, sender):
        objectName, actionName, cmd, result = msg
        if self.rpcClients.get((objectName,actionName,cmd)):
            self.send("rpcResult",msg,self.rpcClients[(objectName,actionName,cmd)].pop())

server = Server()
try:
    while not server.shutdown:
        server.pump()
finally:
    server.kill()
